package com.yilin.tms.user.controller;

import com.yilin.commons.encrypt.EncryptUtils;
import com.yilin.commons.encrypt.Md5Util;
import com.yilin.commons.exception.BusinessException;
import com.yilin.commons.util.StreamUtil;
import com.yilin.commons.util.StringUtil;
import com.yilin.tms.core.application.AppUtil;
import com.yilin.tms.core.application.ReturnData;
import com.yilin.tms.core.commons.base.BaseController;
import com.yilin.tms.core.commons.captcha.CaptchaData;
import com.yilin.tms.core.commons.captcha.CaptchaUtil;
import com.yilin.tms.core.commons.constant.HttpConstant;
import com.yilin.tms.core.commons.annotation.AbnormalVerify;
import com.yilin.tms.core.commons.annotation.SkipLoginVerify;
import com.yilin.tms.core.commons.utils.ParamUtil;
import com.yilin.tms.core.commons.utils.SMSUtil;
import com.yilin.tms.core.commons.utils.UserUtil;
import com.yilin.tms.core.components.redis.RedisClient;
import com.yilin.tms.core.components.redis.RedisKeys;
import com.yilin.tms.core.entity.user.account.Account;
import com.yilin.tms.core.entity.user.UserType;
import com.yilin.tms.user.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;

/***
 * API认证鉴权控制器
 * 用户鉴权中心
 *
 *  提供了4中鉴权方式
 *
 * */
@RestController
@RequestMapping("/user/login/")
public class LoginController extends BaseController {

    @Autowired
    private IAccountService accountService;

    public static final String PW_LENGTH_PATTERN="^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$";

    /**
     * 壳测试
     */
    @PostMapping("testLogin")
    @SkipLoginVerify //不需要登录就可以访问
    public ReturnData testLogin(String username, String password) {
        if (StringUtil.isEmpty(username)) {
            return jsonView(false, "壳测试登录失败！用户名为空！");
        }
        if (StringUtil.isEmpty(password)) {
            return jsonView(false, "壳测试登录失败！密码为空!");
        }
        Account account = new Account();
        account.setUsername(username);
        account.setPassword(password);
        return jsonView(true, "壳测试登录返回成功！", account);
    }
    /**
     *获取登录信息
     */
    @PostMapping("getLoginInfo")
    public ReturnData getLoginInfo() {
        Account account=accountService.getAccountById(getLoginUser().getId());
        return jsonView(true, "获取登录信息！", account);
    }


    /**
     * 统一密码登录管理
     */
    @PostMapping("passLogin")
    @SkipLoginVerify //不需要登录就可以访问
    @AbnormalVerify //默认1分钟允许访问3次
    public ReturnData passLogin(Account.TerminalType terminalType, UserType userType, String username,
                                String imgCode, String imgId, String password, String deviceId, String pushId) {
        if (StringUtil.isEmpty(username)) {
            return jsonView(false, "请输入用户名/账号");
        }
        if (StringUtil.isEmpty(password)) {
            return jsonView(false, "请输入密码");
        }
        if (StringUtil.isNotEmpty(deviceId) && StringUtil.isEmpty(pushId)) {
            return jsonView(false, "移动端需验证推送ID");
        }
        if (!StringUtil.isNotEmpty(deviceId) && (imgCode == null || imgCode.length() != 4)) {
            return jsonView(false, "请输入4位数图片验证码");
        }
        //检查验证码是否正确
        if (!StringUtil.isNotEmpty(deviceId) && !CaptchaUtil.validateCaptchaCode(imgId, imgCode)) {
            CaptchaUtil.removeCaptchaCode(imgId);  // 清除验证码
            return jsonView(FAIL_CODE, "验证码不正确");
        }
        //检查客户账号是否存在
        Account account = null;
        if (userType != null && AppUtil.isNumeric(username) && username.length() == 11) {
            account = accountService.getAccountByPhone(username, userType);
        }
        if (account == null) account = accountService.getAccountByUsername(username);
        if (account == null) return jsonView(NO_REGISTER, "账号未注册！请先完成注册");
//        if (account.getUserType()!=userType) return jsonView(FAIL_CODE, "登录终端验证失败");
        //检查deviceId是否正确
//        if (isMobile && !deviceId.equals(account.getDeviceId())) {
//            return jsonView(NO_PASS_LOGIN, "检测到您已更换设备，本次登录存在风险，请使用手机验证码登录");
//        }
        //检查密码
        ParamUtil.isNull(account.getPassword(), "登录密码未设置，请使用手机验证码登录");
        if (!account.getPassword().equals(Md5Util.md5(password))) {
            return jsonView(LOGIN_PASS_ERR, "账号或密码不正确");
        }
        //更新极光推送ID
        account.setDeviceId(deviceId);
        account.setTerminalType(terminalType);
        //先保存登录token
        UserUtil.saveLoginUser(account);
        accountService.login(account, request);
        account.setPassword(null);
        account.setSafePassword(null);
        account.setSecretKey(null);
        if (!password.matches(PW_LENGTH_PATTERN)){
            return jsonView(true, "密码过于简单请尽快修改密码！！！", account, account.getToken());
        }
        return jsonView(true, "登录成功", account, account.getToken());
    }

    /**
     * 统一手机登录管理
     */
    @PostMapping("phoneLogin")
    @SkipLoginVerify //不需要登录就可以访问
    public ReturnData phoneLogin(Account.TerminalType terminalType, UserType userType, String phone, String verCode, String deviceId, String pushId) {
        if (StringUtil.isEmpty(phone) || !AppUtil.isNumeric(phone) || phone.length() != 11) {
            return jsonView(false, "请输入11位正确大陆手机号");
        }
        if (StringUtil.isEmpty(verCode)) {
            return jsonView(false, "请输入您收到手机验证码");
        }
        if (StringUtil.isNotEmpty(deviceId) && StringUtil.isEmpty(pushId)) {
            return jsonView(false, "移动端需验证推送ID");
        }
        //检查客户账号是否存在
        Account account = accountService.getAccountByPhone(phone, userType);
        if (account == null) return jsonView(false, "手机号未注册！请先完成注册");
//        if (account.getUserType()!=userType) return jsonView(FAIL_CODE, "登录终端验证失败");
        //检查验证码
        Map<String, Object> verifyCode = SMSUtil.verifyCode(phone, verCode, SMSUtil.MsgType.phoneLogin);
        if (!(Boolean) verifyCode.get(SMSUtil.SUCCESS)) {
            return jsonView(FAIL_CODE, (String) verifyCode.get(SMSUtil.MSG));
        }
        account.setTerminalType(terminalType);
        account.setDeviceId(deviceId);
        //更新极光推送ID
        UserUtil.saveLoginUser(account);
        accountService.login(account, request);
        account.setPassword(null);
        account.setSafePassword(null);
        account.setSecretKey(null);
        return jsonView(true, "登录成功", account, account.getToken());
    }

    /**
     * 统一微信登录管理
     */
    @PostMapping("wechatLogin")
    @SkipLoginVerify //不需要登录就可以访问
    public ReturnData wechatLogin(Account.TerminalType terminalType, String wechatId, String deviceId, String pushId) {
        if (StringUtil.isEmpty(wechatId)) {
            return jsonView(false, "请跳转微信完成登录");
        }
        if (StringUtil.isNotEmpty(deviceId) && StringUtil.isEmpty(pushId)) {
            return jsonView(false, "移动端需验证推送ID");
        }
        if (StringUtil.isNotEmpty(deviceId) && StringUtil.isEmpty(deviceId)) {
            return jsonView(false, "移动端需验证设备号");
        }
        //检查客户账号是否存在
        Account account = accountService.getAccountByWechatId(wechatId);
        if (account == null) {
            return jsonView(false, "未找到账户！请先完成手机号注册，然后在“我的>设置>用户信息”开启微信登录！");
        }
        account.setDeviceId(deviceId);
        account.setTerminalType(terminalType);
        UserUtil.saveLoginUser(account);
        accountService.login(account, request);
        account.setPassword(null);
        account.setSecretKey(null);
        account.setSafePassword(null);
        return jsonView(true, "登录成功", account, account.getToken());
    }

    /**
     * 统一人脸登录管理
     */
    @PostMapping("faceLogin")
    @SkipLoginVerify //不需要登录就可以访问
    public ReturnData faceLogin(Account.TerminalType terminalType, UserType userType, String username, String base64FaceImg, String deviceId, String pushId) {
        if (true) return jsonView(false, "抱歉，人脸登录正在维护中，请使用其他登录方式！");
        if (StringUtil.isEmpty(username)) {
            return jsonView(false, "请输入用户名/账号/手机号");
        }
        if (StringUtil.isNotEmpty(deviceId) && StringUtil.isEmpty(deviceId)) {
            return jsonView(false, "移动端需验证设备号");
        }
        if (StringUtil.isNotEmpty(deviceId) && StringUtil.isEmpty(pushId)) {
            return jsonView(false, "移动端需验证推送ID");
        }
        if (!StreamUtil.isImageFromBase64(base64FaceImg)) {
            return jsonView(false, "请提供正确的人脸数据");
        }
        //检查客户账号是否存在
        Account account = accountService.getAccountByUsername(username);
        //容错处理 手机号容错处理
        if (account == null && username.length() == 11 && AppUtil.isNumeric(username)) {
            account = accountService.getAccountByPhone(username, userType);
        }
        if (account == null) {
            return jsonView(NO_REGISTER, "账号未注册！请先完成注册");
        }
        //去人脸接口识别，识别度大于多少（一般分数要超过90）视为识别成功
        double score = accountService.faceSimilarityScore(account, base64FaceImg);
        if (score < 80) {
            return jsonView(NO_REGISTER, "人脸识别失败，请重新发起识别");
        }
        account.setTerminalType(terminalType);
        account.setDeviceId(deviceId);
        UserUtil.saveLoginUser(account);
        accountService.login(account, request);
        account.setPassword(null);
        account.setSecretKey(null);
        account.setSafePassword(null);
        return jsonView(true, "登录成功", account, account.getToken());
    }

    /**
     * api授权密码登录
     * 客户授权的时候 客户端不放密码 而是放公钥 密码为私钥 让其配对
     * 这里为了更安全，公钥和私钥都上了一层新密码
     * 注意：注册API用户时，要为用户生成密钥对 密钥对需二次加密
     */
    @PostMapping("keyLogin")
    @SkipLoginVerify //不需要登录就可以访问
    public ReturnData keyLogin(String username, String customerKey) {
        //检查客户账号是否存在
        Account account = accountService.getAccountByUsername(username);
        if (account == null) return jsonErrView("账号未注册！");
        if (account.getSecretKey() == null) return jsonErrView("账号不支持API秘钥登录！");
        try {
            //解密客户公钥KAY 多了一层加密
            String publicKey = EncryptUtils.aesDecrypt(customerKey, null);
            //从服务器获取私钥 SecretKey
            String customerPrivateKey = account.getSecretKey();
            //解密客户私钥KAY 多了一层加密
            String privateKey = EncryptUtils.aesDecrypt(customerPrivateKey, null);
            //检查秘钥是否配对
            boolean match = EncryptUtils.validRSAKeyPairMatch(publicKey, privateKey);
            if (!match) return jsonErrView("客户授权验证失败，请检查“customerKey”是否正确！");
        } catch (Exception e) {
            return jsonErrView("客户授权验证失败，请检查授权码是否正确！");
        }
        account.setTerminalType(Account.TerminalType.grant);
        account.setLoginIps(AppUtil.getIp(request));
        UserUtil.saveLoginUser(account);
        accountService.login(account, request);
        account.setPassword(null);
        account.setSecretKey(null);
        account.setSafePassword(null);
        return jsonView(true, "授权成功", account, account.getToken());
    }

    /**
     * 退出登录
     */
    @PostMapping(value = "/logout")
    @SkipLoginVerify
    public ReturnData<Object> logout() {
        UserUtil.removeLoginUser(request.getHeader(HttpConstant.TOKEN));
        return jsonView(true, "退出登录成功");
    }

    /**
     * 获取图片验证码
     */
    @RequestMapping(value = "/getImageCode")
    @SkipLoginVerify
    public ReturnData<Object> getImageCode() {
        CaptchaData captchaData = CaptchaUtil.generateCaptchaCode();
        return jsonView(true, "获取图片成功", captchaData);
    }

    /**
     * 安全公共验证：生成滑动验证图片
     */
    @RequestMapping("generateCaptchaSlide")
    @SkipLoginVerify
    public ReturnData<Object> generateCaptchaSlide() {
        try {
            Map<String, Object> stringMap = CaptchaUtil.generateCaptchaSlide();
            return jsonView(true, "获取成功", stringMap);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("创建滑动验证失败:"+e.getMessage());
        }
    }

    /**
     * 安全公共验证：生成滑动验证图片
     */
    @RequestMapping("validateCaptchaSlide")
    @SkipLoginVerify
    public ReturnData<Object> validateCaptchaSlide(String captchaSlideId,Integer xPosition) {
        if (captchaSlideId==null || xPosition==null) return jsonView(true, "获取成功", false);
        boolean captchaSlide = CaptchaUtil.validateCaptchaSlide(captchaSlideId, xPosition);
        if (captchaSlide) RedisClient.getInstance().hdel(RedisKeys.HTTP_MAN_MACHINE_KEY, AppUtil.getIp(request));
        return jsonView(true, "获取成功", captchaSlide);
    }
}
